﻿using Quartz;
using Quartz.Impl;
using Quartz.Impl.AdoJobStore.Common;
using Quartz.Impl.AdoJobStore;
using Quartz.Impl.Matchers;
using Quartz.Simpl;
using Quartz.Spi;
using Quartz.Util;

using RuoVea.ExDto;
using RuoVea.ExEnum;
using RuoVea.ExUtil;
using RuoVea.ExUtil.Exceptions;
using System.Reflection;
using RuoVea.QuartzNetUI.Server.Dto;
using RuoVea.QuartzNetUI.Server.Enums;
using RuoVea.QuartzNetUI.Job;
using NetTaste;
using Quartz.Impl.Triggers;

namespace RuoVea.QuartzNetUI.Server.Scheduler
{
    /// <summary>
    /// 计划任务中心
    /// </summary>
    public class TaskSchedulerServer : ITaskSchedulerServer
    {
        private readonly IScheduler _scheduler;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="schedulerFactory"></param>
        public TaskSchedulerServer(ISchedulerFactory schedulerFactory)
        {
            _scheduler = schedulerFactory.GetScheduler().Result;
        }

        /// <summary>
        /// 任务开启
        /// </summary>
        /// <returns></returns>
        /// <exception cref="ParamiterException"></exception>
        public async Task<RestfulResult> StartTaskScheduleAsync()
        {
            RestfulResult result = new RestfulResult();
            try
            {
                var IsStarted = _scheduler.IsStarted;
                if (IsStarted)
                    throw new ParamiterException($"计划任务已经开启");

                //等待任务运行完成
                await _scheduler.Start();
                result.Message = $"计划任务开启成功";

            }
            catch (Exception ex)
            {
                result.Code = CodeStatus.BadRequest;
                result.Message = "Source:" + ex.Source + " StackTrace:" + ex.StackTrace + " Message:" + ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 停止计划任务
        /// </summary>
        /// <returns></returns>
        public async Task<RestfulResult> StopTaskScheduleAsync()
        {
            RestfulResult result = new RestfulResult();
            try
            {
                if (_scheduler.IsShutdown)
                    throw new ParamiterException($"计划任务已经停止");
                await _scheduler.Shutdown();
                result.Message = $"计划任务已经停止";
            }
            catch (Exception ex)
            {

                result.Code = CodeStatus.BadRequest;
                result.Message = "Source:" + ex.Source + " StackTrace:" + ex.StackTrace + " Message:" + ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public RestfulResult ValidateTaskDetail(TaskDetailInDto data)
        {

            RestfulResult result = new RestfulResult() { Code = CodeStatus.OK };

            if (data.TriggerType == TriggerTypeEnum.Simple)
            {
                if (data.IntervalSecond == 0)
                {
                    result.Code = CodeStatus.BadRequest;
                    result.Message = "执行间隔时间需大于零";
                    return result;
                }
            }

            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool CheckExists(TaskDetailInDto data) {
            JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);
            return  _scheduler.CheckExists(jobKey).Result;
        }
        /// <summary>
        /// 添加一个计划任务
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<RestfulResult> AddTaskScheduleAsync(TaskDetailInDto data)
        {
            RestfulResult result = new RestfulResult();
            try
            {
                result = ValidateTaskDetail(data);
                if (result.Code != CodeStatus.OK)
                    return result;
                else
                    result.Code = CodeStatus.OK;

                JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);

                bool IsExists = await _scheduler.CheckExists(jobKey);
                if (IsExists)
                {
                    result.Message = $"该计划任务已经在执行:【{data.JobName}】,请勿重复添加！";
                    result.Code = CodeStatus.BadRequest;
                    return result;
                }

                #region 设置开始时间和结束时间

                data.BeginTime = data.BeginTime == null ? DateTime.Now : data.BeginTime;
                data.EndTime = data.EndTime == null ? DateTime.MaxValue.AddDays(-1) : data.EndTime;

                DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(data.BeginTime, 1);//设置开始时间
                DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(data.EndTime, 1);//设置暂停时间

                #endregion
                Type? jobType = null;
                if (data.RunType == RequestTypeEnum.Run)
                {
                    #region 通过反射获取程序集类型和类
                    //传入反射出来的执行程序集
                    Assembly assembly = Assembly.Load(new AssemblyName(data.AssemblyName));
                    jobType = assembly.GetType(data.AssemblyName + "." + data.ClassName);
                    #endregion
                }
                else
                {
                    if (data.ScheduleType == ScheduleTypeEnum.Http)
                    {
                        jobType = typeof(HttpJob);
                    }
                    else if (data.ScheduleType == ScheduleTypeEnum.GRPC)
                    {

                    }
                }

                IJobDetail job = new JobDetailImpl(data.Id + "", data.JobGroup, jobType);
                ITrigger trigger;

                if (data.TriggerType == TriggerTypeEnum.Corn)
                {
                    if (string.IsNullOrWhiteSpace(data.Cron))
                        throw new Exception("Cron表达式不能为空.");
                    if (!CronExpression.IsValidExpression(data.Cron))
                        throw new Exception("Cron表达式错误.");

                    trigger = CreateCronTrigger(data);
                }
                else
                    trigger = CreateSimpleTrigger(data);

                job.JobDataMap.SetRunType(data.RunType ?? RequestTypeEnum.Run);
                job.JobDataMap.SetJobData(data);
                // 告诉Quartz使用我们的触发器来安排作业
                var scheduleJob = await _scheduler.ScheduleJob(job, trigger);
                //判断任务调度是否开启
                var IsStarted = _scheduler.IsStarted;
                if (!IsStarted)
                    await StartTaskScheduleAsync();

                result.Message = $"启动计划任务:【{data.JobName}】成功！";
            }
            catch (Exception ex)
            {
                result.Code = CodeStatus.BadRequest;
                result.Message = "Source:" + ex.Source + " StackTrace:" + ex.StackTrace + " Message:" + ex.Message;
            }
            return result;
        }
        /// <summary>
        /// 暂停指定的计划任务
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<RestfulResult> PauseTaskScheduleAsync(TaskDetailInDto data)
        {
            RestfulResult result = new RestfulResult();
            try
            {
                JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);
                if (!await _scheduler.CheckExists(jobKey))
                    throw new ParamiterException($"未找计划任务:【{data.JobName}】");

                await _scheduler.PauseJob(jobKey);
                result.Message = $"暂停计划任务:【{data.JobName}】成功";
            }
            catch (Exception ex)
            {

                result.Code = CodeStatus.BadRequest;
                result.Message = "Source:" + ex.Source + " StackTrace:" + ex.StackTrace + " Message:" + ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 恢复指定计划任务
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<RestfulResult> ResumeTaskScheduleAsync(TaskDetailInDto data)
        {
            RestfulResult result = new RestfulResult();
            try
            {
                JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);
                if (!await _scheduler.CheckExists(jobKey))
                    throw new ParamiterException($"未找到计划任务:【{data.JobName}】");

                await _scheduler.ResumeJob(jobKey);
                result.Message = $"恢复计划任务:【{data.JobName}】成功";
            }
            catch (Exception ex)
            {

                result.Code = CodeStatus.BadRequest;
                result.Message = "Source:" + ex.Source + " StackTrace:" + ex.StackTrace + " Message:" + ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 删除指定计划任务
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<RestfulResult> DeleteTaskScheduleAsync(TaskDetailInDto data)
        {
            RestfulResult result = new RestfulResult();
            try
            {
                if (data.Id <= 0) throw new ArgumentException($"{nameof(data.Id)} 数据不能为空.");
                if (string.IsNullOrWhiteSpace(data.JobName)) throw new ArgumentException($"{nameof(data.JobName)} 数据不能为空.");
                if (string.IsNullOrWhiteSpace(data.JobGroup)) throw new ArgumentException($"{nameof(data.JobGroup)} 数据不能为空.");

                JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);
                await _scheduler.DeleteJob(jobKey);
                result.Message = $"删除计划任务:【{data.JobName}】成功";
            }
            catch (Exception ex)
            {

                result.Code = CodeStatus.BadRequest;
                result.Message = "Source:" + ex.Source + " StackTrace:" + ex.StackTrace + " Message:" + ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 立即运行
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<RestfulResult> RunTaskScheduleAsync(TaskDetailInDto data)
        {
            RestfulResult result = new RestfulResult();
            try
            {
                JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);

                List<JobKey> jobKeys = _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(data.JobGroup)).Result.ToList();
                if (jobKeys == null || jobKeys.Count == 0)
                    await AddTaskScheduleAsync(data);

                var triggers = await _scheduler.GetTriggersOfJob(jobKey);
                if (triggers.Count <= 0)
                    throw new ParamiterException($"未找到触发器[{data.JobName}]");

                //ITrigger trigger = triggers.FirstOrDefault();
                //if (taskOptions != null && taskOptions.Status != (int)TriggerState.Normal)
                //{
                //    await _scheduler.ResumeTrigger(trigger.Key);
                //}
                //else
                //{
                //}
                 
                    await _scheduler.TriggerJob(jobKey);
                result.Message = $"运行计划任务:【{data.JobName}】成功";
            }
            catch (Exception ex)
            {
                result.Code = CodeStatus.BadRequest;
                result.Message = $"执行计划任务:【{data.JobName}】失败，{ex.Message}";
            }
            return result;
        }

        /// <summary>
        /// 更新计划任务
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task<RestfulResult> UpdateTaskScheduleAsync(TaskDetailInDto data)
        {
            RestfulResult result = new RestfulResult();
            try
            {
                JobKey jobKey = new JobKey(data.Id + "", data.JobGroup);
                if (await _scheduler.CheckExists(jobKey))
                {
                    //防止创建时存在数据问题 先移除，然后在执行创建操作
                    await _scheduler.DeleteJob(jobKey);
                }
                //await AddTaskScheduleAsync(data);
                result.Message = "修改计划成功";
            }
            catch (Exception ex)
            {
                result.Code = CodeStatus.BadRequest;
                result.Message = $"修改计划:【{data.JobName}】失败，{ex.Message}";
            }
            return result;
        }

        #region 创建触发器帮助方法

        /// <summary>
        /// 创建SimpleTrigger触发器（简单触发器）
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private ITrigger CreateSimpleTrigger(TaskDetailInDto data)
        {
            if (data.RunTimes > 0)
            {
                ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity(data.Id + "", data.JobGroup)
                .StartAt(data.BeginTime.Value)
                .EndAt(data.EndTime.Value)
                .WithSimpleSchedule(x =>
                x.WithIntervalInSeconds(data.IntervalSecond ?? 0)
                .WithRepeatCount(data.RunTimes ?? 0)).ForJob(data.Id + "", data.JobGroup).Build();
                return trigger;
            }
            else
            {
                ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity(data.Id + "", data.JobGroup)
                .StartAt(data.BeginTime.Value)
                .EndAt(data.EndTime.Value)
                .WithSimpleSchedule(x =>
                x.WithIntervalInSeconds(data.IntervalSecond ?? 0)
                .RepeatForever()).ForJob(data.Id + "", data.JobGroup).Build();
                return trigger;
            }
            // 触发作业立即运行，然后每10秒重复一次，无限循环

        }

        /// <summary>
        /// 创建类型Cron的触发器
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private ITrigger CreateCronTrigger(TaskDetailInDto data)
        {
            // 作业触发器
            return TriggerBuilder.Create()
                   .WithIdentity(data.Id + "", data?.JobGroup)
                   .StartAt(data.BeginTime.Value)//开始时间
                   .EndAt(data?.EndTime.Value)//结束数据
                   .WithCronSchedule(data?.Cron)//指定cron表达式
                   .ForJob(data.Id + "", data.JobGroup)//作业名称
                   .Build();
        }
        #endregion
    }
}
